#pragma once

#include <cppunit/config/SourcePrefix.h>
#include <iostream>
#include <string>
#include <algorithm>
#include "TestConstantTable.h"

void ConstantTableTest::setUp(){
	/*****************Constant*******************/
	validConInput.push_back(1);
	validConInput.push_back(2);
	validConInput.push_back(3);
	validConInput.push_back(4);
	validConInput.push_back(5);
	validConInput.push_back(2);
	validConInput.push_back(3);
	validConInput.push_back(4);
	validConInput.push_back(5);
	validConInput.push_back(6);

	invalidConInput.push_back(-1);

	nonExistConInput.push_back(99);

	/******************Line*******************/
	validLineInput.push_back(1);
	validLineInput.push_back(1);
	validLineInput.push_back(1);
	validLineInput.push_back(1);
	validLineInput.push_back(1);
	validLineInput.push_back(2);
	validLineInput.push_back(3);
	validLineInput.push_back(3);
	validLineInput.push_back(4);
	validLineInput.push_back(5);

	invalidLineInput.push_back(0);
	invalidLineInput.push_back(-1);

	nonExistLineInput.push_back(99);

	/******************LineEntity*******************/
	validEntityInput.push_back(Type::Entity::ASSIGNMENT_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::ASSIGNMENT_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::ASSIGNMENT_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::ASSIGNMENT_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::ASSIGNMENT_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::IF_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::ASSIGNMENT_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::ASSIGNMENT_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::WHILE_LINE_ENTITY);
	validEntityInput.push_back(Type::Entity::IF_LINE_ENTITY);

	invalidEntityInput.push_back(Type::Entity::INVALID_LINE_ENTITY);

	conTb = ConstantTable();
}

void ConstantTableTest::tearDown(){
	validConInput.clear();
	invalidConInput.clear();
	nonExistConInput.clear();

	validLineInput.clear();
	invalidLineInput.clear();
	nonExistLineInput.clear();

	validEntityInput.clear();
}

// Registers the fixture into the 'registry'
 CPPUNIT_TEST_SUITE_REGISTRATION( ConstantTableTest );


void ConstantTableTest::testInsert(Type::List::Line lineList, Type::List::LineEntity entityList, 
	Type::List::Constant conList, bool pass, std::string failMsg){
		Type::Index::Line tmpLine;
		Type::Entity::Line tmpEntity;
		Type::Constant tmpCon;
		unsigned int t, testNumber;
		bool tmpTF;

		if(lineList.size() > entityList.size()){
			if(lineList.size() > conList.size()){
				testNumber = lineList.size();
			}else{
				testNumber = conList.size();
			}
		}else{
			if(entityList.size() > conList.size()){
				testNumber = entityList.size();
			}else{
				testNumber = conList.size();
			}
		}

		for(t =0 ; t < testNumber; t++){
			tmpLine = lineList.at(Utilities::getIndexBoundedBySize(t, lineList.size()));
			tmpEntity = entityList.at(Utilities::getIndexBoundedBySize(t, entityList.size()));
			tmpCon = conList.at(Utilities::getIndexBoundedBySize(t, conList.size()));

			tmpTF = conTb.insertConstant(tmpLine, tmpEntity, tmpCon);

			if(!pass){
				tmpTF = !tmpTF;
			}

			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg, CPPUNIT_ASSERT(tmpTF));
		}
}

void ConstantTableTest::testGetLines(Type::Constant con, Type::Entity::Line entity, 
	bool isEntityPresent, Type::List::Line lineList, std::string failMsg){
		Type::List::Line tmpLineList, bufLineList;
		Type::Index::Line bufLine;
		unsigned int t;
		bool tmpTF;

		if(!isEntityPresent){
			tmpLineList = conTb.getLinesByConstant(con);
		}else{
			tmpLineList = conTb.getLinesByConstant(con,entity);
		}

		tmpTF = lineList.size() == tmpLineList.size();

		CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (size different)", 
			CPPUNIT_ASSERT(tmpTF));

		for(t = 0; t< lineList.size();t++){
			bufLine = lineList.at(t);

			bufLineList = Utilities::removeElementFromList(tmpLineList, bufLine, true);
			tmpTF = bufLineList.size() < tmpLineList.size();
			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (missing line)", 
				CPPUNIT_ASSERT(tmpTF));

			tmpLineList = bufLineList;
		}

		CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (extra line)", 
			CPPUNIT_ASSERT(tmpLineList.empty()));
}

void ConstantTableTest::testGetConstants(Type::Index::Line line, bool isLinePresent, 
	Type::List::Constant conList, std::string failMsg){
		Type::List::Constant tmpConList, bufConList;
		Type::Constant bufCon;
		unsigned int t;
		bool tmpTF;

		if(isLinePresent){
			tmpConList = conTb.getConstantsByLine(line);
		}else{
			tmpConList = conTb.getConstants();
		}

		tmpTF = conList.size() == tmpConList.size();
		CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (size different)", 
			CPPUNIT_ASSERT(tmpTF));

		for(t = 0; t< conList.size();t++){
			bufCon = conList.at(t);

			bufConList = Utilities::removeElementFromList(tmpConList, bufCon, true);
			tmpTF = bufConList.size() < tmpConList.size();
			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (missing constant)", 
				CPPUNIT_ASSERT(tmpTF));

			tmpConList = bufConList;
		}

		CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg + " (extra constant)", 
			CPPUNIT_ASSERT(tmpConList.empty()));
}

void ConstantTableTest::testIsExists(Type::List::Line lineList, Type::List::Constant conList, 
	bool pass, std::string failMsg){
		Type::Index::Line tmpLine;
		Type::Constant tmpCon;
		unsigned int t;
		bool tmpTF;

		for(t =0 ; t < lineList.size(); t++){
			tmpLine = lineList.at(t);
			tmpTF = conTb.isLineExistsInConstant(tmpLine);

			if(!pass){
				tmpTF = !tmpTF;
			}

			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(
				failMsg + " (line existence)" ,CPPUNIT_ASSERT(tmpTF));
		}

		for(t =0 ; t < conList.size(); t++){
			tmpCon = conList.at(t);
			tmpTF = conTb.isConstantExistsInLine(tmpCon);

			if(!pass){
				tmpTF = !tmpTF;
			}
			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(
				failMsg + " (constant existence)" ,CPPUNIT_ASSERT(tmpTF));
		}
}

// method to test the insertion of constant, line
void ConstantTableTest::testInsert(){
	std::cout<<std::endl<<"ConstantTable Insert()"<<std::endl;

	// verify insertion of invalid line, valid entity, constant will always return false;
	msg = "Failed at insertion of invalid line, valid entity, constant";
	testInsert(invalidLineInput, validEntityInput, validConInput, false, msg);

	// verify insertion of invalid entity, valid line, constant will always return false;
	msg = "Failed at insertion of invalid entity, valid line, constant";
	testInsert(validLineInput, invalidEntityInput, validConInput, false, msg);

	// verify insertion of invalid constant, valid line, entity will always return false;
	msg = "Failed at insertion of invalid constant, valid line, entity";
	testInsert(validLineInput, validEntityInput, invalidConInput, false, msg);

	// verify insertion of valid line, entity, constant will always return true;
	msg = "Failed at insertion of valid line, entity, constant";
	testInsert(validLineInput, validEntityInput, validConInput, true, msg);

	// verify insertion of repeating valid line, entity, constant will always return false;
	msg = "Failed at insertion of repeating valid line, entity, constant";
	testInsert(validLineInput, validEntityInput, validConInput, false, msg);
}

// method to test the retrieval of constant, line
void ConstantTableTest::testGet(){
	std::cout<<std::endl<<"ConstantTable Get()"<<std::endl;

	Type::List::Constant bufConList;
	Type::List::LineEntity bufEntityList;
	Type::List::Line tmpLineList, bufLineList;

	Type::Entity::Line tmpEntity;
	Type::Index::Line tmpLine;
	Type::Constant tmpCon;
	unsigned int t;

	testInsert();

	// verify all constants retrival will always correct;
	bufConList = Utilities::copyListWithoutDuplicates(validConInput);
	msg = "Failed at getting lines of valid constant";
	testGetConstants(0, false, bufConList, msg);

	// verify constant retrival of valid line will always correct;
	bufLineList = Utilities::copyListWithoutDuplicates(validLineInput);
	msg = "Failed at getting constants of valid line";
	for(t =0 ; t < bufLineList.size(); t++){
		tmpLine = bufLineList.at(t);

		bufConList = Utilities::copyListWithValueInSource(validLineInput, tmpLine, validConInput);
		bufConList = Utilities::copyListWithoutDuplicates(bufConList);

		testGetConstants(tmpLine, true, bufConList, msg);
	}

	// verify lines retrival of valid constant will always correct;
	bufConList = Utilities::copyListWithoutDuplicates(validConInput);
	msg = "Failed at getting lines of valid constant";
	for(t =0 ; t < bufConList.size(); t++){
		tmpCon = bufConList.at(t);

		bufLineList = Utilities::copyListWithValueInSource(validConInput, tmpCon, validLineInput);
		bufLineList = Utilities::copyListWithoutDuplicates(bufLineList);

		testGetLines(tmpCon, Type::Entity::INVALID_LINE_ENTITY, false, bufLineList, msg);
	}

	msg = "Failed at getting lines of valid constant with given entity";
	for(t =0 ; t < validConInput.size(); t++){
		tmpCon = validConInput.at(t);
		tmpEntity = validEntityInput.at(Utilities::getIndexBoundedBySize(t, validEntityInput.size()));

		tmpLineList = Utilities::copyListWithValueInSource(validConInput, tmpCon, validLineInput);
		bufLineList = Utilities::copyListWithValueInSource(validEntityInput, tmpEntity, validLineInput);
		tmpLineList = Utilities::mergeListWithIntersection(tmpLineList, bufLineList);
		tmpLineList = Utilities::copyListWithoutDuplicates(tmpLineList);

		testGetLines(tmpCon, tmpEntity, true, tmpLineList, msg);
	}
}

// method to test the existence of constant, line
void ConstantTableTest::testIsExists(){
	std::cout<<std::endl<<"ConstantTable Exist()"<<std::endl;

	testInsert();

	// verify existence of inserted valid line, constant
	msg = "Failed checking existence of inserted line, constant";
	testIsExists(validLineInput, validConInput, true, msg);

	// verify existence of not inserted line, constant
	msg = "Failed checking existence of not inserted line, constant";
	testIsExists(nonExistLineInput, nonExistConInput, false, msg);

	// verify existence of invalid line, constant
	msg = "Failed checking existence of invalid line, constant";
	testIsExists(invalidLineInput, invalidConInput, false, msg);

}